from argo.jdom import *
from argo.jdom import JsonNodeFactories
from java.lang import Class, Boolean, IllegalStateException
from java.util import ArrayList
import scala
from scala import Option, Function1
from pyson.gen import PysonObjectMeta, PysonObject
from pyson.serverMessages import EditContext, EditCookie, Hints, Result, End, ProjectEntries
from scaladuct import CompositeCookieBean
from chloroform import CompositeJsonCookieHandler

#cookieFragmentBuilders = ()
import pyson.serverMessages
import java

def classOf(className):
    return Class.forName(className)

NONE = Option.apply(None)

class CookieFragmentBuilder(object):
    def getCookieMessageFragmentClass(self, cookieGateway, property):
        return None

    def getCookieResponseFragmentClass(self, cookieGateway, property):
        return None

class ProjectEntryFragmentBuilder(object):
    def getProjectEntryFragmentClass(self, projectGateway, projectEntry):
        return None

class MessageFragment(object):
    def __init__(self):
        super(MessageFragment, self).__init__()

class ResponseFragment(object):
    def __init__(self): pass

    def updateProperty(self, jsonNode): pass

class FilterProperties(Function1):
    def __init__(self, jsonHndlr, gateway):
        self.jsonHandler = jsonHndlr
        self.cookieGateway = gateway

    def apply(self, propValue):
        if propValue.refTypeInfo() is not None and \
           propValue.refTypeInfo().name() is not None and \
           propValue.refTypeInfo().name() != "":
            return Boolean.FALSE

        extOpt = self.cookieGateway.pageExtension(classOf("scaladuct.reflect.CookieReflection"))
        if extOpt is NONE:
            return Boolean.TRUE
        else:
            return extOpt.get().propertyInfo(propValue).enabled()

class FilterProjectEntries(Function1):
    def __init__(self, scenario):
        self.scenario = scenario

    def apply(self, projectEntry):
        if self.scenario is None:
            return projectEntry.isPublic()
        else:
            return projectEntry.isPublic() and projectEntry.isInScenario(self.scenario)

class HandleProperties(Function1):
    def __init__(self, extFragBuilders, fieldBuilders, gateway, factoryMethods, fieldBuilderCallback):
        self.extFragBuilders = extFragBuilders
        self.fieldBuilders = fieldBuilders
        self.gateway = gateway
        self.factoryMethods = factoryMethods
        self.fieldBuilderCallback = fieldBuilderCallback

    def apply(self, prop):
        activeExtBuilders = ()
        # composing extensions
        for extFragBuilderCls in self.extFragBuilders:
            extFragBuilder = extFragBuilderCls()
            for factoryMethod in self.factoryMethods:
                fragClass = getattr(extFragBuilder, factoryMethod)(self.gateway, prop)
                if fragClass is not None:
                    activeExtBuilders += (fragClass,)

        if len(activeExtBuilders) == 0:
        # in case there is no extension add one dummy ext
            activeExtBuilders = (MessageFragment,)

        activeFieldBuilders = () # atm there is at most one field builder class
        for fieldFragBuilderCls in self.fieldBuilders:
            fieldFragBuilder = fieldFragBuilderCls()
            for factoryMethod in self.factoryMethods:
                fragClass = getattr(fieldFragBuilder, factoryMethod)(self.gateway, prop)
                if fragClass is not None:
                    activeFieldBuilders += (fragClass,)

            if len(activeFieldBuilders) > 0:
            # break the loop after the first builder is found
                break

        if len(activeFieldBuilders) == 0:
            raise IllegalStateException("No field builder for property " + `prop` + \
            " (factory methods: " + `self.factoryMethods` + ")")

        fieldBuilder = PysonObjectMeta("PysonFieldBuilder", activeExtBuilders + activeFieldBuilders, {})()
        self.fieldBuilderCallback(prop, fieldBuilder)

class ProjectEntriesBuildContext:
    def __init__(self, projectGateway, jsonHandler):
        self.projectGateway = projectGateway
        self.handler = jsonHandler

class JsonHandler(CompositeJsonCookieHandler):
    extensionFragmentBuilders = ()
    fieldBuilders = ()
    hintItemFragmentExtBuilders = ()
    hintItemBuilders = ()
    projectEntryFragmentBuilders = ()
    projectEntryExtensionFragmentBuilders = ()

    def getCookieMessageBuilder(self, cookieGateway):
        "@sig public getCookieMessageBuilder(scaladuct.ConfigPageGateway cookieGateway)"
        return self.getCookieMessage(cookieGateway).createBuilder()

    def getCookieMessage(self, cookieGateway):
        cookie = cookieGateway.getCookie()
        cookieBean = CompositeCookieBean(cookie)

        fieldBuilders = []
        propHandler = HandleProperties(self.extensionFragmentBuilders, self.fieldBuilders, cookieGateway, \
        ("getCookieMessageFragmentClass",), lambda property, fieldBuilder: fieldBuilders.append(fieldBuilder))

        cookieBean.props().filter(FilterProperties(self, cookieGateway)).foreach(propHandler)

        return EditCookie(EditContext(cookieGateway.contextName(), cookieGateway.id(), cookieGateway.canUnconsume()), \
        fieldBuilders)

    def updateCookie(self, cookieGateway, updatedNodesMap):
        updatedProperties = ArrayList()
        def updatePropertyCallback(property, fieldUpdater):
            keyNode = JsonNodeFactories.aJsonString(property.name())
            valueNode = updatedNodesMap.get(keyNode)
            if valueNode is not None:
                updatedProperty = fieldUpdater.updateProperty(valueNode)
                updatedProperties.add(updatedProperty)

        cookie = cookieGateway.getCookie()
        cookieBean = CompositeCookieBean(cookie)

        propHandler = HandleProperties(self.extensionFragmentBuilders, self.fieldBuilders, cookieGateway, \
        ("getCookieResponseFragmentClass",), updatePropertyCallback)
        cookieBean.props().filter(FilterProperties(self, cookieGateway)).foreach(propHandler)

        updatedBean = cookieBean.update(updatedProperties)
        return updatedBean.cookie()

    def getHintsBuilder(self, cookieGateway, hints):
        hintsBuilder = self.getHints(cookieGateway, hints).createBuilder()
        return hintsBuilder

    def getHints(self, cookieGateway, hints):
        itemBuilders = []
        itemHandler = HandleProperties(self.hintItemFragmentExtBuilders, self.hintItemBuilders, cookieGateway, \
        ("getCookieMessageFragmentClass",), lambda hint, itemBuilder: itemBuilders.append(itemBuilder))
        hints.foreach(itemHandler)

        return Hints(EditContext(cookieGateway.contextName(), cookieGateway.id(), cookieGateway.canUnconsume()), \
        itemBuilders)

    def updateHints(self, cookieGateway, hints, responseNode):
        selectedHintId = responseNode.getStringValue("id")
        valueKeyNode = JsonNodeFactories.aJsonString("value")
        newValue = responseNode.getFields().get(valueKeyNode)

        updatedHints = []
        def updateHintCallback(hint, hintUpdater):
            if hintUpdater.id == selectedHintId:
                updatedHints.append(hint)
                if hasattr(hintUpdater, "updateProperty"):
                    hintUpdater.updateProperty(newValue)

        itemHandler = HandleProperties(self.hintItemFragmentExtBuilders, self.hintItemBuilders, cookieGateway, \
        ("getCookieMessageFragmentClass", "getCookieResponseFragmentClass"), updateHintCallback)
        # todo: using findFirst would suit better
        hints.foreach(itemHandler)

        assert len(updatedHints) > 0

        return updatedHints[0]

    def getResultBuilder(self, cookieGateway):
        return self.getResult(cookieGateway).createBuilder()

    def getResult(self, cookieGateway):
        return Result(EditContext(cookieGateway.contextName(), cookieGateway.id(), cookieGateway.canUnconsume()), \
        "%s" % cookieGateway.instance())

    def getEndBuilder(self):
        return self.getEnd().createBuilder()

    def getEnd(self):
        return End()

    def getProjectEntriesBuilder(self, projectGateway, scenario = None):
        return self.getProjectEntries(projectGateway, scenario).createBuilder()

    def getProjectEntries(self, projectGateway, scenario = None):
        filteredEntries = projectGateway.getEntries().filter(FilterProjectEntries(scenario))

        projectEntryBuilders = self.getBuildersForProjectEntries(ProjectEntriesBuildContext(projectGateway, self), filteredEntries)
        return ProjectEntries(projectEntryBuilders)

    def getSelectedProjectEntriesBuilder(self, projectGateway, entries):
        return self.getSelectedProjectEntries(projectGateway, entries).createBuilder()

    def getSelectedProjectEntries(self, projectGateway, entries):
        projectEntryBuilders = self.getBuildersForProjectEntries(ProjectEntriesBuildContext(projectGateway, self), entries)
        return ProjectEntries(projectEntryBuilders)

    def getBuildersForProjectEntries(self, buildContext, entries):
        projectEntryBuilders = []

        entriesHandler = HandleProperties(self.projectEntryExtensionFragmentBuilders, self.projectEntryFragmentBuilders, buildContext, \
        ("getProjectEntryFragmentClass",), lambda projectEntry, projectEntryBuilder: projectEntryBuilders.append(projectEntryBuilder))

        entries.foreach(entriesHandler)

        return projectEntryBuilders
